<?php

namespace app\common\service;

use Qcloud\Cos\Client;
use QCloud\COSSTS\Sts;

class CosService extends ApiService
{

    /**
     * cos桶
     * @var mixed
     */
    private mixed $bucket;
    /**
     * cos区域
     * @var mixed
     */
    private mixed $region;
    /**
     * cos secretId
     * @var mixed
     */
    private mixed $secretId;
    /**
     * cos secretKey
     * @var mixed
     */
    private mixed $secretKey;
    /**
     * cos endpoint
     * @var mixed
     */
    private mixed $endpoint;

    private string $type;

    public function __construct()
    {
        $this->bucket = env('cos.cos_bucket');
        $this->region = env('cos.cos_region');
        $this->secretId = env('cos.cos_secret_id');
        $this->secretKey = env('cos.cos_secret_key');
        $this->endpoint = env('cos.cos_endpoint');
        $this->type = 'tencent';
    }

    public function getDomain()
    {
        return $this->endpoint;
    }
    /**
     * 获取cos配置
     * @return array
     */
    private function getConfig(): array
    {
        return [
            'region' => $this->region, // COS 区域
            'credentials' => [
                'secretId' => $this->secretId,
                'secretKey' => $this->secretKey,
            ],
        ];
    }

    /**
     * 获取cos客户端
     * @return Client
     */
    private function getClient(): Client
    {
        $config = $this->getConfig();
        return new Client($config);
    }

    public function getObject($path)
    {
        $url = parse_url($path);
        $cosKey = $url['path'];
        try {
            $client = $this->getClient();
            $res = $client->getObject([
                'Bucket' => $this->bucket,
                'Key' => $cosKey,
            ]);
            $res = $res['Body'];
        } catch (\Exception $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $res;
    }

    public function putObject($key, $filePath)
    {
        return $this->simpleUpload($key, $filePath);

        // $partSize = 50 * 1024 * 1024; // 50MB
        // // 获取文件大小
        // $fileSize = filesize($filePath);
        //
        // // 小文件直接使用普通上传
        // if ($fileSize <= $partSize) {
        //     return $this->simpleUpload($key, $filePath);
        // } else {
        //     return $this->shardingUpload($key, $filePath, $fileSize, $partSize);
        // }
    }

    /**
     * 简单上传
     * @param $key
     * @param $filePath
     * @return false|string
     */
    public function simpleUpload($key, $fileContent)
    {
        try {
            $cosClient = $this->getClient();
            $result = $cosClient->putObject([
                'Bucket' => $this->bucket,
                'Key' => $key,
                'Body' => $fileContent,
            ]);
            $result = $this->endpoint . '/' . $result['Key'];
        } catch (\Exception $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $result;
    }


    /**
     * 分片上传
     * @param $key
     * @param $filePath
     * @param $partSize
     * @param $fileSize
     * @param $progressCallback
     * @return false|string
     */
    public function shardingUpload($key, $filePath, $partSize, $fileSize, $progressCallback = null)
    {
        $cosClient = $this->getClient();
        try {
            // 小文件直接使用普通上传
            if ($fileSize <= $partSize) {
                return $this->putObject($key, $filePath);
            }

            // 初始化分片上传
            $result = $cosClient->createMultipartUpload([
                'Bucket' => $this->bucket,
                'Key' => $key,
            ]);

            $uploadId = $result['UploadId'];

            // 计算分片数量
            $partCount = intval(ceil($fileSize / $partSize));

            // 存储每个分片的上传结果
            $uploadedParts = [];

            // 逐个上传分片
            for ($i = 0; $i < $partCount; $i++) {
                $startByte = $i * $partSize;
                $partSizeActual = min($partSize, $fileSize - $startByte);

                // 读取分片数据
                $fileHandle = fopen($filePath, 'rb');
                fseek($fileHandle, $startByte);
                $partData = fread($fileHandle, $partSizeActual);
                fclose($fileHandle);

                // 上传分片
                $partResult = $cosClient->uploadPart([
                    'Bucket' => $this->bucket,
                    'Key' => $key,
                    'Body' => $partData,
                    'UploadId' => $uploadId,
                    'PartNumber' => ($i + 1),
                ]);

                // 保存分片信息
                $uploadedParts[] = [
                    'PartNumber' => ($i + 1),
                    'ETag' => $partResult['ETag'],
                ];

                // 进度回调
                if ($progressCallback && is_callable($progressCallback)) {
                    $progress = round(($i + 1) / $partCount * 100, 2);
                    call_user_func($progressCallback, $progress, ($i + 1), $partCount);
                }
            }

            // 按PartNumber排序（确保顺序正确）
            usort($uploadedParts, function ($a, $b) {
                return $a['PartNumber'] - $b['PartNumber'];
            });

            // 完成分片上传
            $result = $cosClient->completeMultipartUpload([
                'Bucket' => $this->bucket,
                'Key' => $key,
                'UploadId' => $uploadId,
                'MultipartUpload' => [
                    'Parts' => $uploadedParts,
                ],
            ]);

            // 返回文件访问URL
            return $this->endpoint . '/' . $result['Key'];

        } catch (\Exception $e) {
            // 如果上传失败，取消分片上传任务
            if (isset($uploadId)) {
                try {
                    $cosClient->abortMultipartUpload([
                        'Bucket' => $this->bucket,
                        'Key' => $key,
                        'UploadId' => $uploadId,
                    ]);
                } catch (\Exception $abortEx) {
                }
            }
            $this->setError($e->getMessage());
            return false;
        }
    }

    public function getType()
    {
        return $this->type;
    }

    public function getInfo()
    {
        return [
            'domain' => $this->endpoint,
            'bucket' => $this->bucket,
            'region' => $this->region
        ];
    }

    public function getSts()
    {
        try {
            $config = [
                'endpoint' => 'tencentcloudapi.com', // 接入点，内网填写"internal.tencentcloudapi.com"，外网填写"tencentcloudapi.com"
                'proxy' => '',
                'secretId' => $this->secretId, // 固定密钥,若为明文密钥，请直接以'xxx'形式填入，不要填写到getenv()函数中
                'secretKey' => $this->secretKey, // 固定密钥,若为明文密钥，请直接以'xxx'形式填入，不要填写到getenv()函数中
                'bucket' => $this->bucket, // 换成你的 bucket
                'region' => $this->region, // 换成 bucket 所在园区
                'durationSeconds' => 3600, // 密钥有效期
                'allowPrefix' => ["*"], // 只分配当前 key 的路径权限
                // 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
                'allowActions' => [
                    // 简单上传
                    'name/cos:PutObject',
                    // 分片上传
                    'name/cos:InitiateMultipartUpload',
                    'name/cos:ListMultipartUploads',
                    'name/cos:ListParts',
                    'name/cos:UploadPart',
                    'name/cos:CompleteMultipartUpload'
                ],
            ];
            $sts = new Sts();
            $tempKeys = $sts->getTempKeys($config);
        } catch (\Exception $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $tempKeys;

    }

}
